package nebula

import (
	"fmt"
	"runtime/debug"

	"gddgame.cc/galaxy/utils"
)

type Middleware func(socket *Socket, next Next) error

type ConnectHandler func(socket *Socket, next Next)
type DisconnectHandler func(socket *Socket, reason string)

type Namespace interface {
	Root() Namespace
	Path() NSP
	On(event NSE, handler interface{}) error
	Trigger(event NSE, params []interface{}, req *Request) (*Request, error)
	Use(middleware Middleware) Namespace
	Of(nsp NS) (Namespace, error)
	SetCompress(compress bool)

	Connect(handler ConnectHandler) error
	Disconnect(handler DisconnectHandler) error
}

// 用于处理事件分发，逻辑业务管理
type serverNamespace struct {
	server        *Server
	ns            NS
	nsp           NSP
	fns           []Middleware
	nss           []*serverNamespace
	socketChannel chan *Socket
	compress      bool
	crypto        bool

	connectHandler    ConnectHandler
	disconnectHandler DisconnectHandler

	event utils.Event
	close chan struct{}
}

func newServerNamespace(e *Server, nsp NSP) *serverNamespace {
	ns := NS(0)
	l := len(nsp)
	if l > 0 {
		ns = NS(nsp[len(nsp)-1])
	}
	n := &serverNamespace{
		e,
		ns,
		nsp,
		[]Middleware{},
		[]*serverNamespace{},
		//make(map[string]*socketInline),
		make(chan *Socket, 3),
		//protocol.DefaultProtocol,
		false,
		false,
		nil,
		nil,
		utils.NewEvent(),
		make(chan struct{}, 1),
	}
	n.listenChannel()
	return n
}

func (n *serverNamespace) Root() Namespace {
	return n.server.root
}

func (n *serverNamespace) Path() NSP {
	return n.nsp
}

func (n *serverNamespace) Use(middleware Middleware) Namespace {
	n.fns = append(n.fns, middleware)
	return n
}

func (n *serverNamespace) On(event NSE, handler interface{}) error {
	return n.event.On(event, handler)
}

func (n *serverNamespace) Trigger(event NSE, params []interface{}, req *Request) (res *Request, err error) {
	return req.Socket.Trigger(event, params)
}

func (n *serverNamespace) Connect(handler ConnectHandler) error {
	n.connectHandler = handler
	return nil
}

func (n *serverNamespace) Disconnect(handler DisconnectHandler) error {
	n.disconnectHandler = handler
	return nil
}

func (n *serverNamespace) Of(ns NS) (Namespace, error) {
	return n.of(ns)
}

func (n *serverNamespace) of(ns NS) (*serverNamespace, error) {
	if ns == NSP_DEFAULT {
		return n, nil
	}
	for _, nsI := range n.nss {
		if nsI.ns == ns {
			return nsI, fmt.Errorf("namespace %b already used", ns)
		}
	}
	nsI := newServerNamespace(n.server, append(n.nsp, byte(ns)))
	n.nss = append(n.nss, nsI)

	return nsI, nil
}

func (n *serverNamespace) With(nsp NSP) (*serverNamespace, error) {
	if nsp == nil {
		return n, nil
	}
	//n.e.Debugf("select namespace:#v", nsp)
	nn := n
	for _, ns := range nsp {
		nn, _ = (nn).with(NS(ns))
		if nn == nil {
			return nil, fmt.Errorf("namespace %s not exist", nsp)
		}
	}
	return nn, nil
}

func (n *serverNamespace) with(ns NS) (*serverNamespace, error) {
	if ns == NSP_DEFAULT {
		return n, nil
	}
	for _, nsI := range n.nss {
		if nsI.ns == ns {
			return nsI, nil
		}
	}
	return nil, fmt.Errorf("namespace %b not exist", ns)
}

func (n *serverNamespace) SetCrypto(crypto bool) {
	n.crypto = crypto
}

func (n *serverNamespace) SetCompress(compress bool) {
	n.compress = compress
}

func (n *serverNamespace) Close() error {
	if n.nss == nil {
		return nil
	}
	n.close <- struct{}{}
	for k, _ := range n.nss {
		if err := n.nss[k].Close(); err != nil {
			return err
		}
	}
	n.nss = nil
	return nil
}

func (n *serverNamespace) listenChannel() {
	go func() {
		for {
			select {
			case socket := <-n.socketChannel:
				n.connect(socket)
			case <-n.close:
				return
			}
		}
	}()
}

func (n *serverNamespace) run(socket *Socket, next Next) {
	l := len(n.fns)
	if l == 0 {
		_ = next(nil)
		return
	}
	_ = n.m(socket, l, 0, next)
}

func (n *serverNamespace) m(socket *Socket, all int, index int, next Next) error {
	return n.fns[index](socket, func(err error) error {
		if err != nil {
			return next(err)
		} else if index+1 >= all {
			return next(nil)
		} else {
			return n.m(socket, all, index+1, next)
		}
	})
}

func (n *serverNamespace) connect(socket *Socket) {
	n.run(socket, func(err error) error {
		if err != nil {
			return err
		}
		if n.connectHandler != nil {
			n.connectHandler(socket, func(err error) error {
				if err != nil {
					socket.SendError(err)
					socket.Disconnect()
				} else {
					socket.onConnect()
				}
				return err
			})
		}
		return nil
	})
}

func (n *serverNamespace) disconnect(socket *Socket, reason string) {
	if n.disconnectHandler != nil {
		n.disconnectHandler(socket, reason)
	}
}

func (n *serverNamespace) fire(event interface{}, req *Request, params []interface{}) error {
	defer func() {
		if err := recover(); err != nil {
			debug.PrintStack()
			fmt.Println("namespace error", err, n.ns, n.nsp, n.event)
		}
	}()
	params = append([]interface{}{req}, params...)

	_, err := n.event.Fire(event, params)
	return err
}
